//===-- RegisterContextMemory.cpp -------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "RegisterContextMemory.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "DynamicRegisterInfo.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/RegisterValue.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;

//----------------------------------------------------------------------
// RegisterContextMemory constructor
//----------------------------------------------------------------------
RegisterContextMemory::RegisterContextMemory
(
    Thread &thread,
    uint32_t concrete_frame_idx,
    DynamicRegisterInfo &reg_infos,
    addr_t reg_data_addr
) :
    RegisterContext (thread, concrete_frame_idx),
    m_reg_infos (reg_infos),
    m_reg_valid (),
    m_reg_data (),
    m_reg_data_addr (reg_data_addr)
{
    // Resize our vector of bools to contain one bool for every register.
    // We will use these boolean values to know when a register value
    // is valid in m_reg_data.
    const size_t num_regs = reg_infos.GetNumRegisters();
    assert (num_regs > 0);
    m_reg_valid.resize (num_regs);

    // Make a heap based buffer that is big enough to store all registers
    DataBufferSP reg_data_sp(new DataBufferHeap (reg_infos.GetRegisterDataByteSize(), 0));
    m_reg_data.SetData (reg_data_sp);
}

//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
RegisterContextMemory::~RegisterContextMemory()
{
}

void
RegisterContextMemory::InvalidateAllRegisters ()
{
    if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
        SetAllRegisterValid (false);
}

void
RegisterContextMemory::SetAllRegisterValid (bool b)
{
    std::vector<bool>::iterator pos, end = m_reg_valid.end();
    for (pos = m_reg_valid.begin(); pos != end; ++pos)
        *pos = b;
}

size_t
RegisterContextMemory::GetRegisterCount ()
{
    return m_reg_infos.GetNumRegisters ();
}

const RegisterInfo *
RegisterContextMemory::GetRegisterInfoAtIndex (size_t reg)
{
    return m_reg_infos.GetRegisterInfoAtIndex (reg);
}

size_t
RegisterContextMemory::GetRegisterSetCount ()
{
    return m_reg_infos.GetNumRegisterSets ();
}

const RegisterSet *
RegisterContextMemory::GetRegisterSet (size_t reg_set)
{
    return m_reg_infos.GetRegisterSet (reg_set);
}

uint32_t
RegisterContextMemory::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
{
    return m_reg_infos.ConvertRegisterKindToRegisterNumber (kind, num);
}

bool
RegisterContextMemory::ReadRegister (const RegisterInfo *reg_info, RegisterValue &reg_value)
{
    const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
    if (!m_reg_valid[reg_num])
    {                         
        if (!ReadAllRegisterValues(m_reg_data.GetSharedDataBuffer ()))
            return false;
    }
    const bool partial_data_ok = false;
    return reg_value.SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok).Success();
}

bool
RegisterContextMemory::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &reg_value)
{
    if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
    {
        const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
        addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset;
        Error error (WriteRegisterValueToMemory(reg_info, reg_addr, reg_info->byte_size, reg_value));
        m_reg_valid[reg_num] = false;
        return error.Success();
    }
    return false;
}

bool
RegisterContextMemory::ReadAllRegisterValues (DataBufferSP &data_sp)
{
    if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
    {
        ProcessSP process_sp (CalculateProcess());
        if (process_sp)
        {
            Error error;
            if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize())
            {
                SetAllRegisterValid (true);
                return true;
            }
        }
    }
    return false;
}

bool
RegisterContextMemory::WriteAllRegisterValues (const DataBufferSP &data_sp)
{
    if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
    {
        ProcessSP process_sp (CalculateProcess());
        if (process_sp)
        {
            Error error;
            SetAllRegisterValid (false);
            if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize())
                return true;
        }
    }
    return false;
}

void
RegisterContextMemory::SetAllRegisterData  (const lldb::DataBufferSP &data_sp)
{
    m_reg_data.SetData(data_sp);
    SetAllRegisterValid (true);    
}
